package fuzzy;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import enums.*;

public class FuzzyEngine {

	double Vdesired;
	double Ddesired;

	Fuzzifier fuzzifier;
	FuzzyInferer fuzzyInferer;
	Defuzzifier defuzzifier;

	List<FuzzyRule> rules = new LinkedList<FuzzyRule>();
	Map<Velocity, FuzzyMemFunction> vMemFuncs = new HashMap<Velocity, FuzzyMemFunction>();
	Map<Distance, FuzzyMemFunction> dMemFuncs = new HashMap<Distance, FuzzyMemFunction>();
	Map<Acceleration, FuzzyMemFunction> aMemFuncs = new HashMap<Acceleration, FuzzyMemFunction>();

	public FuzzyEngine(double Vdesired, double Ddesired) {

		this.Vdesired = Vdesired;
		this.Ddesired = Ddesired;
		createMemFunctions();
		createFuzzyRules();
	}

	private void createMemFunctions() {
		vMemFuncs.put(Velocity.VERY_SLOW,
				FuzzyMemFunction.newReversedSigmoid("", 5, Vdesired - 20));
		vMemFuncs.put(Velocity.SLOW,
				FuzzyMemFunction.newGaussian("", 5, Vdesired - 10));
		vMemFuncs.put(Velocity.JUST_RIGHT,
				FuzzyMemFunction.newGaussian("", 5, Vdesired));
		vMemFuncs.put(Velocity.FAST,
				FuzzyMemFunction.newGaussian("", 5, Vdesired + 10));
		vMemFuncs.put(Velocity.VERY_FAST,
				FuzzyMemFunction.newSigmoid("", 5, Vdesired + 20));

		dMemFuncs.put(Distance.VERY_FAR,
				FuzzyMemFunction.newReversedSigmoid("", 5, Ddesired - 20));
		dMemFuncs.put(Distance.FAR,
				FuzzyMemFunction.newGaussian("", 5, Ddesired - 10));
		dMemFuncs.put(Distance.JUST_RIGHT,
				FuzzyMemFunction.newGaussian("", 5, Ddesired));
		dMemFuncs.put(Distance.CLOSE,
				FuzzyMemFunction.newGaussian("", 5, Ddesired + 10));
		dMemFuncs.put(Distance.VERY_CLOSE,
				FuzzyMemFunction.newSigmoid("", 5, Ddesired + 20));

		aMemFuncs.put(Acceleration.BRAKE_HARD,
				FuzzyMemFunction.newReversedSigmoid("", 5, -20));
		aMemFuncs.put(Acceleration.BRAKE,
				FuzzyMemFunction.newGaussian("", 5, -10));
		aMemFuncs
				.put(Acceleration.NONE, FuzzyMemFunction.newGaussian("", 5, 0));
		aMemFuncs.put(Acceleration.ACCELERATE,
				FuzzyMemFunction.newGaussian("", 5, 10));
		aMemFuncs.put(Acceleration.ACCELERATE_HARD,
				FuzzyMemFunction.newSigmoid("", 5, 20));
	}

	private void createFuzzyRules() {

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_SLOW), dMemFuncs
				.get(Distance.VERY_FAR), aMemFuncs
				.get(Acceleration.ACCELERATE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_SLOW), dMemFuncs
				.get(Distance.FAR), aMemFuncs.get(Acceleration.ACCELERATE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_SLOW), dMemFuncs
				.get(Distance.JUST_RIGHT), aMemFuncs
				.get(Acceleration.ACCELERATE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_SLOW), dMemFuncs
				.get(Distance.CLOSE), aMemFuncs.get(Acceleration.ACCELERATE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_SLOW), dMemFuncs
				.get(Distance.VERY_CLOSE), aMemFuncs
				.get(Acceleration.ACCELERATE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.SLOW), dMemFuncs
				.get(Distance.VERY_FAR), aMemFuncs
				.get(Acceleration.ACCELERATE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.SLOW), dMemFuncs
				.get(Distance.FAR), aMemFuncs.get(Acceleration.ACCELERATE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.SLOW), dMemFuncs
				.get(Distance.JUST_RIGHT), aMemFuncs
				.get(Acceleration.ACCELERATE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.SLOW), dMemFuncs
				.get(Distance.CLOSE), aMemFuncs.get(Acceleration.ACCELERATE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.SLOW), dMemFuncs
				.get(Distance.VERY_CLOSE), aMemFuncs
				.get(Acceleration.ACCELERATE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.JUST_RIGHT), dMemFuncs
				.get(Distance.VERY_FAR), aMemFuncs.get(Acceleration.ACCELERATE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.JUST_RIGHT), dMemFuncs
				.get(Distance.FAR), aMemFuncs.get(Acceleration.ACCELERATE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.JUST_RIGHT), dMemFuncs
				.get(Distance.JUST_RIGHT), aMemFuncs.get(Acceleration.NONE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.JUST_RIGHT), dMemFuncs
				.get(Distance.CLOSE), aMemFuncs.get(Acceleration.BRAKE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.JUST_RIGHT), dMemFuncs
				.get(Distance.VERY_CLOSE), aMemFuncs.get(Acceleration.BRAKE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.FAST), dMemFuncs
				.get(Distance.VERY_FAR), aMemFuncs.get(Acceleration.BRAKE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.FAST), dMemFuncs
				.get(Distance.FAR), aMemFuncs.get(Acceleration.BRAKE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.FAST), dMemFuncs
				.get(Distance.JUST_RIGHT), aMemFuncs.get(Acceleration.BRAKE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.FAST), dMemFuncs
				.get(Distance.CLOSE), aMemFuncs.get(Acceleration.BRAKE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.FAST), dMemFuncs
				.get(Distance.VERY_CLOSE), aMemFuncs
				.get(Acceleration.BRAKE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_FAST), dMemFuncs
				.get(Distance.VERY_FAR), aMemFuncs.get(Acceleration.BRAKE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_FAST), dMemFuncs
				.get(Distance.FAR), aMemFuncs.get(Acceleration.BRAKE)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_FAST), dMemFuncs
				.get(Distance.JUST_RIGHT), aMemFuncs
				.get(Acceleration.BRAKE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_FAST), dMemFuncs
				.get(Distance.CLOSE), aMemFuncs.get(Acceleration.BRAKE_HARD)));

		rules.add(new FuzzyRule(vMemFuncs.get(Velocity.VERY_FAST), dMemFuncs
				.get(Distance.VERY_CLOSE), aMemFuncs
				.get(Acceleration.BRAKE_HARD)));

	}

	public void testRules(double velocity, double distance) {
		for (FuzzyRule rule : rules) {
			determineRuleMin(rule, velocity, distance);
		}
	}

	private double determineRuleMin(FuzzyRule rule, double vi, double di) {
		System.out.println(rule.v.getMemValue(vi) + "\t"
				+ rule.d.getMemValue(di));
		return Math.min(rule.v.getMemValue(vi), rule.d.getMemValue(di));
	}

}
